package haiyan.common.email;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.mail.BodyPart;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;

import haiyan.common.ByteUtil;
import haiyan.common.DebugUtil;
import haiyan.common.FileUtil;
import haiyan.common.StringUtil;
import haiyan.common.exception.Warning;

/**
 * @author zhouxw
 * 
 */
@SuppressWarnings("unused")
public class EmailAcceptor {

	private int mailCounter;

	private int mailIndex; //

	private int mailDownErrorCounter; //

	private boolean[] recordFailure; //

	private int totalRetryTimes; //

	private int retryTimeCounter; //

	private boolean otherError; //

	private String extension = ".eml";//

	private Store store;

	private Folder folder;

	private Message[] messages;

	private Message message;

	private Part part;

	private String emlName;

	private String attachName;

	private int allMessageCount;

	private int messageCount;

	private String dateformat; //

	// private String propFile =
	// MailConstants.PROPS_FILE_NAME;//

	private String protocol = "pop3"; //

	private String mailHost; //

	private String userName; //

	private String password; //

	private String saveAttachPath; //

	private String saveEmlPath;//

	public EmailAcceptor() throws Throwable {
		saveEmlPath = System.getProperty("user.dir") + "" + File.separator + "upload"
				+ File.separator + "emails";
		/*
		 * FileProperties fp = new FileProperties(propFile); fp.load(); protocol =
		 * fp.getProperty(MailConstants.RECV_PROTO); mailHost =
		 * fp.getProperty(MailConstants.RECV_HOST); userName =
		 * fp.getProperty(MailConstants.RECV_USER); password =
		 * fp.getProperty(MailConstants.RECV_PASS); saveAttachPath =
		 * fp.getProperty(MailConstants.RECV_ATTACH); saveEmlPath =
		 * fp.getProperty(MailConstants.RECV_ROOT); dateformat =
		 * fp.getProperty("mail.receive.dtfmat"); extension =
		 * fp.getProperty("mail.receive.extension"); totalRetryTimes = Integer
		 * .parseInt(fp.getProperty("mail.receive.retry"));
		 */
	}
	public void setMailHost(String mailHost) {
		this.mailHost = mailHost;
	}
	public String getMailHost() {
		return this.mailHost;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getUserName() {
		return this.userName;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public void setStore(Store store) {
		this.store = store;
	}
	public void setFolder(Folder folder) {
		this.folder = folder;
	}
	public void setMessages(Message[] messages) {
		this.messages = messages;
	}
	public void setMessage(Message message) {
		this.message = message;
	}
	public void setCurMessage(int i) {
		this.message = this.messages[i];
	}
	public Message getMessage() {
		return this.message;
	}
	public int getAllMessageCount() throws MessagingException {
		this.allMessageCount = folder.getMessageCount();
		return allMessageCount;
	}
	public void setAllMessageCount() throws MessagingException {
		this.allMessageCount = this.folder.getMessageCount();
	}
	public int getMessageCount() {
		this.messageCount = this.messages.length;
		return messageCount;
	}
	public int getNewMessageCount() throws MessagingException {
		return this.folder.getNewMessageCount();
	}
	public int getUnreadMessageCount() throws MessagingException {
		return this.folder.getUnreadMessageCount();
	}
	public Part getPart() {
		return (Part) message;
	}
	public void setPart(Part part) {
		this.part = part;
	}
	public void setAttachPath(String attachPath) {
		this.saveAttachPath = attachPath;
	}
	public String getAttachPath() {
		return saveAttachPath;
	}
	public void setEmlPath(String emlPath) {
		this.saveEmlPath = emlPath;
	}
	public String getEmlPath() {
		return this.saveEmlPath;
	}
	public void setEmlName(String emlName) {
		this.emlName = emlName;
	}
	public String getEmlName() {
		return emlName;
	}
	public void setAttachName(String attachName) {
		this.attachName = attachName;
	}
	public String getAttachName() {
		return attachName;
	}
	public void setExtension(String extension) {
		this.extension = extension;
	}
	public String getExtension() {
		return this.extension;
	}
	public void setDateFormat(String format) throws Throwable {
		this.dateformat = format;
	}
	public String getDateFormat(String format) throws Throwable {
		return this.dateformat;
	}
	public String getFrom() throws Throwable {
		return getFrom(this.message);
	}
	public String getFrom(Message mimeMessage) throws Throwable {
		InternetAddress address[] = (InternetAddress[]) mimeMessage.getFrom();
		String from = address[0].getAddress();
		if (from == null)
			from = "";
		String personal = address[0].getPersonal();
		if (personal == null)
			personal = "";
		String fromaddr = personal + "<" + from + ">";
		return fromaddr;
	}
	public String getTOAddress() throws Throwable {
		return getMailAddress("TO", this.message);
	}
	public String getCCAddress() throws Throwable {
		return getMailAddress("CC", this.message);
	}
	public String getBCCAddress() throws Throwable {
		return getMailAddress("BCC", this.message);
	}
	public String getTOAddress(Message mimeMessage) throws Throwable {
		return getMailAddress("TO", mimeMessage);
	}
	public String getCCAddress(Message mimeMessage) throws Throwable {
		return getMailAddress("CC", mimeMessage);
	}
	public String getBCCAddress(Message mimeMessage) throws Throwable {
		return getMailAddress("BCC", mimeMessage);
	}
	public String getMailAddress(String type) throws Throwable {
		return getMailAddress(type, this.message);
	}
	public String getMailAddress(String type, Message mimeMessage)
			throws Throwable {
		String mailaddr = "";
		String addtype = type.toUpperCase();
		InternetAddress[] address = null;
		if (addtype.equals("TO") || addtype.equals("CC") || addtype.equals("BCC")) {
			if (addtype.equals("TO")) {
				address = (InternetAddress[]) mimeMessage.getRecipients(Message.RecipientType.TO);
			} else if (addtype.equals("CC")) {
				address = (InternetAddress[]) mimeMessage.getRecipients(Message.RecipientType.CC);
			} else {
				address = (InternetAddress[]) mimeMessage.getRecipients(Message.RecipientType.BCC);
			}
			if (address != null) {
				for (int i = 0; i < address.length; i++) {
					String email = address[i].getAddress();
					if (email == null)
						email = "";
					else {
						email = MimeUtility.decodeText(email);
					}
					String personal = address[i].getPersonal();
					if (personal == null)
						personal = "";
					else {
						personal = MimeUtility.decodeText(personal);
					}
					String compositeto = personal + "<" + email + ">";
					mailaddr += "," + compositeto;
				}
				mailaddr = mailaddr.substring(1);
			}
		} else {
			throw new Exception("Error emailaddr type!");
		}
		return mailaddr;
	}
	public String getSubject() throws MessagingException {
		return getSubject(this.message);
	}
	public String getSubject(Message mimeMessage) throws MessagingException {
		String subject = "";
		try {
			subject = MimeUtility.decodeText(mimeMessage.getSubject());
			if (subject == null)
				subject = "";
		} catch (Exception exce) {
		}
		return subject;
	}
	public String getSentDate() throws Throwable {
		return getSentDate(this.message);
	}
	public String getSentDate(Message mimeMessage) throws Throwable {
		Date sentdate = mimeMessage.getSentDate();
		SimpleDateFormat format = new SimpleDateFormat(dateformat);
		return format.format(sentdate);
	}
	public boolean getReplySign() throws MessagingException {
		return getReplySign(this.message);
	}
	public boolean getReplySign(Message mimeMessage) throws MessagingException {
		boolean replysign = false;
		String needreply[] = mimeMessage
				.getHeader("Disposition-Notification-To");
		if (needreply != null) {
			replysign = true;
		}
		return replysign;
	}
	public String getMessageId() throws MessagingException {
		return getMessageId(this.message);
	}
	/**
	 * @param mimeMessage
	 * @return String
	 * @throws MessagingException
	 */
	public String getMessageId(Message mimeMessage) throws MessagingException {
		return ((MimeMessage) mimeMessage).getMessageID();
	}
	private void setRecordFailure() {
		this.recordFailure = new boolean[getMessageCount()];
	}
	public boolean[] getRecordFailure() {
		return this.recordFailure;
	}
	public boolean isNew() throws MessagingException {
		return isNew(this.message);
	}
	public boolean isNew(Message mimeMessage) throws MessagingException {
		boolean isnew = false;
		Flags flags = mimeMessage.getFlags();
		Flags.Flag[] flag = flags.getSystemFlags();
		for (int i = 0; i < flag.length; i++) {
			if (flag[i] == Flags.Flag.SEEN) {
				isnew = true;
				break;
			}
		}
		return isnew;
	}
	public boolean isContainAttach() throws Throwable {
		return isContainAttach(this.part);
	}
	public boolean isContainAttach(Part part) throws Throwable {
		boolean attachflag = false;
		if (part.isMimeType("multipart/*")) {
			Multipart mp = (Multipart) part.getContent();
			for (int i = 0; i < mp.getCount(); i++) {
				BodyPart mpart = mp.getBodyPart(i);
				String disposition = mpart.getDisposition();
				if ((disposition != null)
						&& ((disposition.equals(Part.ATTACHMENT)) || (disposition
								.equals(Part.INLINE)))) {
					attachflag = true;
				} else if (mpart.isMimeType("multipart/*")) {
					attachflag = isContainAttach((Part) mpart);
				} else {
					String contype = mpart.getContentType();
					if (contype.toLowerCase().indexOf("application") != -1)
						attachflag = true;
					if (contype.toLowerCase().indexOf("name") != -1)
						attachflag = true;
				}
			}
		} else if (part.isMimeType("message/rfc822")) {
			attachflag = isContainAttach((Part) part.getContent());
		}
		return attachflag;
	}
	public void getConn() throws Throwable {
		try {
			this.getStoreFromServer();
			this.getFolderFromStore();
		} catch (Exception ex) {
			otherError = true;
			mailDownErrorCounter++;
			throw ex;
			// System.out.print(e.getLocalizedMessage());
		}
	}
	private Store getStoreFromServer() throws MessagingException {
		Session session = Session.getDefaultInstance(System.getProperties(), null);
		// session.setDebug(true);
		Store store = session.getStore(protocol);
		DebugUtil.debug(">connecting");
		store.connect(mailHost, userName, password);
		DebugUtil.debug(">connected successfully");
		setStore(store);
		return store;
	}
	private Folder getFolderFromStore() throws MessagingException {
		Folder getFolder = store.getFolder("INBOX");
		getFolder.open(Folder.READ_ONLY);
		setFolder(getFolder);
		return getFolder;
	}
	public void getAllMessages() throws Throwable {
		Message[] messages = folder.getMessages();
		setMessages(messages);
		setRecordFailure();
	}
	public void getMessages(int[] messageNums) throws MessagingException {
		Message[] messages = folder.getMessages(messageNums);
		setMessages(messages);
		setRecordFailure();
		// setMessageCount();
	}
	public void getMessages(int start, int end) throws MessagingException {
		Message[] messages = folder.getMessages(start, end);
		setMessages(messages);
		setRecordFailure();
	}
	public void closeConnection() {
		try {
			messages = null;
			message = null;
			if (folder.isOpen())
				folder.close(true);
			store.close();
			DebugUtil.debug(">close");
		} catch (Throwable ex) {
			ex.printStackTrace();
		}
	}
	public void getMail() throws Throwable {
		try {
			saveMessageAs(message);
			parseMessage(message);
		} catch (Throwable e) {
			throw e;
		}
	}
	public void getMail(int index) {
		mailDownErrorCounter = 0;
		try { 
			setMessage(messages[index]); 
			getMail();
		} catch (Throwable e) {
			e.printStackTrace();
			recordFailure[index] = true;
			mailDownErrorCounter++;
			retry();
		}
	}
	public void getAllMail() {
		int mailArrayLength;
		mailArrayLength = getMessageCount();
		mailDownErrorCounter = 0;
		mailCounter = 0;
		for (int index = 0; index < mailArrayLength; index++) {
			try {
				setMessage(messages[index]);
				getMail();
				mailCounter++;
			} catch (Throwable e) {
				otherError = false;
				recordFailure[index] = true;
				mailDownErrorCounter++;
			}
		}
		mailCounter = 0;
		if (mailDownErrorCounter != 0)
			retry();
	}
	public void saveMessageAs(Message message) throws Throwable {
		// String fileExtend;
		ByteArrayOutputStream baos = null;
		ByteArrayInputStream in = null;
		try {
			String oriFileName = getInfoBetweenBrackets(getMessageId(message).toString());
			String emlName = oriFileName;
			String fileNameWidthExtension = oriFileName + getExtension();
			File storeFile = new File(getEmlPath() + fileNameWidthExtension);
			if (!storeFile.getParentFile().exists()) {
				if (!storeFile.getParentFile().mkdirs()) {
				}
			}
			for (int i = 0; storeFile.exists(); i++) {
				emlName = oriFileName + i;
				fileNameWidthExtension = emlName + getExtension();
				storeFile = new File(getEmlPath() + fileNameWidthExtension);
			}
			setEmlName(emlName);
			DebugUtil.debug(">storefile's path: " + fileNameWidthExtension);
			baos = new ByteArrayOutputStream();
			message.writeTo(baos);
			in = new ByteArrayInputStream(baos.toByteArray());
			saveFile(fileNameWidthExtension, in);
		}
		// catch (MessagingException e) {
		// e.printStackTrace();
		// }
		// catch (Throwable e) {
		// throw e;
		// }
		finally {
			if (in != null)
				try {
					in.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			if (baos != null)
				try {
					baos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}
	public void parseMessage(Message message) throws Throwable {
		Object content = message.getContent();
		if (content instanceof Multipart) {
			handleMultipart((Multipart) content);
		} else {
			handlePart(message);
		}
	}
	public void handleMultipart(Multipart multipart) throws Throwable {
		for (int i = 0, n = multipart.getCount(); i < n; i++) {
			handlePart(multipart.getBodyPart(i));
		}
	}
	/**
	 * @throws MessagingException
	 * @throws IOException
	 */
	public void handlePart() throws Throwable {
		handlePart(this.part);
	}
	public void handlePart(Part part) throws Throwable {
		// Find attachment
		String disposition = part.getDisposition();
		String contentType = part.getContentType();
		// InputStreamReader sbis = new InputStreamReader(
		InputStream sbis = part.getInputStream();
		if (disposition == null) { // When just body
			DebugUtil.debug(">Null: " + contentType);
			// Check if plain
			if ((contentType.length() >= 9)
					&& (contentType.toLowerCase().substring(0, 9).equals("text/plai"))) {
				DebugUtil.debug(">" + getEmlName() + ".txt");
				saveFile(getEmlName() + ".txt", sbis);
			} else if ((contentType.length() >= 8) // Check if html
					&& (contentType.toLowerCase().substring(0, 8).equals("text/htm"))) {
				DebugUtil.debug(">" + getEmlName() + ".html");
				saveFile(getEmlName() + ".html", sbis);
			} else if ((contentType.length() >= 9) // Check if html
					&& (contentType.toLowerCase().substring(0, 9).equals("image/gif"))) {
				DebugUtil.debug(">" + getEmlName() + ".gif");
				saveFile(getEmlName() + ".gif", sbis);
			} else if ((contentType.length() >= 10)
					&& contentType.toLowerCase().substring(0, 10).equals("multipart/")) { // Check if multipart
				DebugUtil.debug(">multipart body: " + contentType);
				// Multipart mp = ;
				handleMultipart((Multipart) (part.getContent()));
			} else { // Unknown type
				DebugUtil.debug(">Other body: " + contentType);
				saveFile(getEmlName() + ".txt", sbis);
			}
		} else if (disposition.equalsIgnoreCase(Part.ATTACHMENT)) {
			DebugUtil.debug(">Attachment: " + part.getFileName() + " : "
					+ contentType);
			saveFile(part.getFileName(), sbis);
		} else if (disposition.equalsIgnoreCase(Part.INLINE)) {
			DebugUtil.debug(">Inline: " + part.getFileName() + " : "
					+ contentType);
			saveFile(part.getFileName(), sbis);
		} else { // Should never happen
			DebugUtil.debug(">Other: " + disposition);
			// outToFile.println("Other: " + disposition);
			throw new Warning("Should never happen");
		}
	}
	public void saveFile(String fileName, InputStream input) throws Throwable {
		if (StringUtil.isBlankOrNull(fileName)) {
			fileName = getAttachPath() + "xxx.eml";
		} else {
			fileName = getAttachPath() + fileName;
		}
		// Do no overwrite existing file
		File file = new File(fileName);
		if (!file.getParentFile().exists()) {
			if (!file.getParentFile().mkdirs()) {
			}
		}
		int lastDot = fileName.lastIndexOf(".");
		String extension = null;
		if (lastDot != -1)
			extension = fileName.substring(lastDot);
		else
			extension = ".dat";
		fileName = fileName.substring(0, lastDot);
		for (int i = 0; file.exists(); i++) {
			file = new File(fileName + i + extension);
		}
		FileUtil.write(file, ByteUtil.getBytes(input));
	}
	public void readEmlFile(String fileName) throws Throwable {
		InputStream fis = null;
		try {
			// readEmlFile
			fis = new FileInputStream(fileName);
			// Object emlObj = (Object) fis;
			Session mailSession = Session.getDefaultInstance(System
					.getProperties(), null);
			MimeMessage msg = new MimeMessage(mailSession, fis);
			message = msg;
		} catch (Throwable ex) {
			throw ex;
		} finally {
			if (fis != null)
				try {
					fis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}
	private String getInfoBetweenBrackets(String str) throws Throwable {
		int i, j;
		if (str == null) {
			str = "error";
			return str;
		}
		i = str.lastIndexOf("<");
		j = str.lastIndexOf(">");
		if (i != -1 && j != -1)
			str = str.substring(i + 1, j);
		return str;
	}
	private void retry() {
		mailCounter = 0;
		while (retryTimeCounter < totalRetryTimes && mailDownErrorCounter != 0) {
			if (!store.isConnected() || !folder.isOpen()) {
				closeConnection();
				return;
			}
			mailDownErrorCounter = 0;
			for (int index = 0; index < getMessageCount(); index++) {
				if (recordFailure[index]) {
					try {
						setMessage(messages[index]);
						getMail();
						mailCounter++;
						recordFailure[index] = false;
					} catch (Throwable e) {
						otherError = false;
						recordFailure[index] = true;
						mailDownErrorCounter++;
					}
				}
			}
			retryTimeCounter++;
		}
		mailCounter = 0;
		mailDownErrorCounter = 0;
	}

}